﻿using UnityEngine;
using System.Collections;
using System.Collections.Generic;

using System;
using System.IO;
using System.Reflection;

using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters;
using System.Runtime.Serialization.Formatters.Binary;

using System.Linq;

namespace SG
{
    public class UnCloneAttribute : Attribute { }
    public static class ReflectionUtils
    {
        static private List<MethodInfo> msResultMethods = new List<MethodInfo>();
        public static MethodInfo[] GetMethods(System.Type classType, System.Type attribute = null)
        {
            MethodInfo[] methods = classType.GetMethods(BindingFlags.Instance
                                                         | BindingFlags.Static
                                                         | BindingFlags.Public
                                                         | BindingFlags.NonPublic);
            if (null == attribute)
            {
                return methods;
            }

            msResultMethods.Clear();
            for (int i = 0; i < methods.Length; i++)
            {
                MethodInfo method = methods[i];
                if (method.IsDefined(attribute, false))
                {
                    msResultMethods.Add(method);
                }
            }

            return msResultMethods.ToArray();
        }

        /// <summary>
        /// 获取某个类的全部子类
        /// </summary>
        /// <param name="baseType"></param>
        /// <returns></returns>
        public static System.Type[] GetSubTypes(System.Type baseType)
        {
            System.Type[] asmTypes = GetUnityAssembly().GetTypes();
            asmTypes = System.Array.FindAll<System.Type>(asmTypes, delegate (System.Type value)
            {
                return value.IsSubclassOf(baseType);
            });

            return asmTypes;
        }

        public static System.Type[] CollectAttributeTypes(System.Type attribute)
        {
            List<System.Type> types = new List<System.Type>();

            System.Type[] asmTypes = GetUnityAssembly().GetTypes();
            foreach (System.Type type in asmTypes)
            {
                object[] attributes = type.GetCustomAttributes(attribute, false);
                if (attributes.Length > 0)
                {
                    types.Add(type);

                    //				U3dSingletonAttribute realInst = attributes[0] as U3dSingletonAttribute;
                    //				Debug.Log ("type.GetType():" + type);
                }
            }

            return types.ToArray();
        }

        public static System.Type[] CollectProtoAttributeTypes(System.Type attribute)
        {
            List<System.Type> types = new List<System.Type>();
            GetUnityAssembly();
            if (mProtoCSAsm == null)
            {
                return types.ToArray();
            }
            System.Type[] asmTypes = mProtoCSAsm.GetTypes();
            foreach (System.Type type in asmTypes)
            {
                object[] attributes = type.GetCustomAttributes(attribute, false);
                if (attributes.Length > 0)
                {
                    types.Add(type);
                }
            }

            return types.ToArray();
        }

        #region Deep clone
        /// <summary>
        /// 深拷贝 测试十万数据时 比递归 差5倍以上的效率？
        /// </summary>
        /// <param name="other"></param>
        /// <returns></returns>
        static public object SerializeClone(object other)
        {
            if (null == other)
            {
                return null;
            }

            Type thisType = other.GetType();
            bool bIsSerialize = thisType.IsSerializable;
            bIsSerialize |= null != thisType.GetInterface("ISerializable", true);
            if (!bIsSerialize)
            {
                Debug.LogWarning("The clone object is not Serializable");
                return null;
            }

            BinaryFormatter tFormatter = new BinaryFormatter(null, new StreamingContext(StreamingContextStates.Clone));

            MemoryStream stream = new MemoryStream();
            tFormatter.Serialize(stream, other);
            stream.Position = 0;

            object newClone = tFormatter.Deserialize(stream);
            stream.Close();

            return newClone;
        }

        static public object ReflectClone(object other)
        {
            System.Type thisType = other.GetType();
            object newClone = Activator.CreateInstance(thisType);

            System.Type tmpType = thisType;
            if (tmpType != null)
            {
                FieldInfo[] fieldInfos = tmpType.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
                foreach (FieldInfo field in fieldInfos)
                {
                    object val = field.GetValue(other);
                    if (null != val && val.GetType() == tmpType)
                    {
                        object val1 = val;
                        field.SetValue(newClone, val1);
                    }
                    else
                    {
                        object val1 = DeepClone(val);
                        field.SetValue(newClone, val1);
                    }
                }

                PropertyInfo[] propInfos = tmpType.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
                foreach (PropertyInfo current in propInfos)
                {
                    if (current.CanRead)
                    {
                        ParameterInfo[] paramInfos = current.GetIndexParameters();
                        if (paramInfos.Length == 0)
                        {
                            object val = current.GetValue(other, null);
                            if (null != val && val.GetType() == tmpType)
                            {
                                Debug.Log(string.Format("val:{0}", val.GetType()));

                                object val1 = val;
                                if (current.CanWrite)
                                {
                                    current.SetValue(newClone, val1, null);
                                }
                            }
                            else
                            {
                                object val1 = DeepClone(val);
                                if (current.CanWrite)
                                {
                                    current.SetValue(newClone, val1, null);
                                }
                            }
                        }
                        else
                        {
                            //foreach (ParameterInfo param in paramInfos)
                            //{
                            //    DebugLogger.Print("Param: ", param.Name, param.Position, param.RawDefaultValue);
                            //    //object val = current.GetValue(other, new object[]{param.Name});
                            //    //object val1 = DeepClone(val);
                            //    //if (current.CanWrite)
                            //    //{
                            //    //    current.SetValue(newClone, val1, new object[] { param.Name });
                            //    //}
                            //}
                        }
                    }
                }
            }
            return newClone;
        }

        /// <summary>
        /// 反射递归 深度拷贝,效率低于序列化 反序列化拷贝，低于ICloneable.Clone
        /// 测试十万数据时 比序列化 好5倍以上的效率？
        /// </summary>
        /// <param name="other"></param>
        /// <returns></returns>
        static public object DeepClone(object other)
        {
            //RuntimeTypeHandle typeHandle = Type.GetTypeHandle(other);
            //Type[] tTypes = Type.GetTypeArray(new object[] {other});

            if (null == other)
            {
                return null;
            }

            Type type = other.GetType();
            if (IsNullable(type)
                && null == other)
            {
                return null;
            }

            if (TypeHasUnCloneAttribute(type))
            {
                return other;
            }

            if (IsPrimitive(type))
            {
                return ClonePrimitive(other);
            }
            else if (IsEnum(type))
            {
                return CloneEnum(other);
            }
            else if (IsString(type))
            {
                return CloneString(other);
            }
            else if (IsArray(type))
            {
                return CloneArray(other as Array);
            }
            else if (IsList(type))
            {
                return CloneList(other as ICollection);
            }
            else if (IsDictionary(type))
            {
                return CloneDictionary(other as IDictionary);
            }
            else if (IsScriptableObject(type))
            {
                return CloneScriptableObject(other);
            }
            else if (IsStruct(type))
            {
                return CloneObject(other);
            }
            else if (IsObject(type))
            {
                return CloneObject(other);
            }

            return null;
        }

        static public bool TypeHasUnCloneAttribute(System.Type type)
        {
            object[] uncloneAttrs = type.GetCustomAttributes(typeof(UnCloneAttribute), true);
            if (uncloneAttrs != null && uncloneAttrs.Length > 0)
            {
                return true;
            }

            return false;
        }

        static public bool MemberHasUnCloneAttribute(MemberInfo info)
        {
            object[] uncloneAttrs = info.GetCustomAttributes(typeof(UnCloneAttribute), true);
            if (uncloneAttrs != null && uncloneAttrs.Length > 0)
            {
                return true;
            }

            return false;
        }

        static public bool IsNullable(Type t)
        {
            if (t.IsValueType)
                return IsNullableType(t);

            return true;
        }
        static public bool IsNullableType(Type t)
        {
            return (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>));
        }

        // 8中基本类型
        static public bool IsPrimitive(System.Type type)
        {
            if (!type.IsValueType)
                return false;

            if (!type.IsPrimitive)
                return false;

            return true;
        }

        static public object ClonePrimitive(object val)
        {
            return val;
        }

        /// <summary>
        /// 枚举类型,枚举都是从System.Enum继承
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        static public bool IsEnum(System.Type type)
        {
            if (!type.IsValueType)
                return false;

            if (type.IsEnum)
                return true;

            bool result = type.IsSubclassOf(typeof(System.Enum));
            return result;
        }

        static public object CloneEnum(object val)
        {
            return val;
        }

        static public bool IsString(System.Type type)
        {
            if (type == typeof(string))
                return true;

            return false;
        }

        static public object CloneString(object other)
        {
            string tmp = other as string;
            string val = string.Copy(tmp);
            return val;
        }

        static public bool IsStruct(System.Type type)
        {
            if (!type.IsValueType)
                return false;

            bool result = type.IsSubclassOf(typeof(System.Object));
            return result;
        }


        static public bool IsObject(System.Type type)
        {
            if (!type.IsClass)
                return false;

            // 判断是否是类或者结构体
            bool result = type.IsSubclassOf(typeof(System.Object));
            return result;
        }

        static public object CloneObject(object other)
        {
            System.Type thisType = other.GetType();
            object newClone = null;

            //// 如果没有默认构造函数，不支持
            //ConstructorInfo[] constructorInfos = thisType.GetConstructors();
            //for (int i = 0; i < constructorInfos.Length; i++)
            //{
            //}
            newClone = Activator.CreateInstance(thisType);

            System.Type tmpType = thisType;
            if (tmpType != null)
            {
                FieldInfo[] fieldInfos = tmpType.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
                foreach (FieldInfo field in fieldInfos)
                {
                    object val = field.GetValue(other);
                    if (MemberHasUnCloneAttribute(field))
                    {
                        field.SetValue(newClone, val);
                    }
                    // 如果一个类中声明了同样类型的变量，且不为空，直接赋值，防止死循环
                    else if (null != val && val.GetType() == tmpType)
                    {
                        object val1 = val;
                        field.SetValue(newClone, val1);
                    }
                    else
                    {
                        object val1 = DeepClone(val);
                        field.SetValue(newClone, val1);
                    }
                }

                PropertyInfo[] propInfos = tmpType.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
                foreach (PropertyInfo current in propInfos)
                {
                    if (current.CanRead && current.CanWrite)
                    {
                        ParameterInfo[] paramInfos = current.GetIndexParameters();
                        if (paramInfos.Length == 0)
                        {
                            object val = current.GetValue(other, null);
                            if (MemberHasUnCloneAttribute(current))
                            {
                                current.SetValue(newClone, val, null);
                            }
                            // 如果一个类中声明了同样类型的变量，且不为空，直接赋值，防止死循环
                            else if (null != val && val.GetType() == tmpType)
                            {
                                //DebugLogger.Print("val:", val.GetType());
                                object val1 = val;
                                current.SetValue(newClone, val1, null);
                            }
                            else
                            {
                                object val1 = DeepClone(val);
                                current.SetValue(newClone, val1, null);
                            }
                        }
                        else
                        {
                            /*
                                public string this[int key]
                                {
                                    get { xxx; }
                                    set { xxxx;}
                                }
                             * 类似的属性变量，会进入到当前逻辑，一般情况下这种属性都会操作一个成员变量，所以可以不被赋值
                            */
                            //DebugLogger.Print("paramInfos.Length:", paramInfos.Length);
                            ////DebugLogger.Print("current.Name:", current.Name);
                            //foreach (ParameterInfo param in paramInfos)
                            //{
                            //    DebugLogger.Print("Param: ", param.Name, param.Position);
                            //    //object val = current.GetValue(other, new object[] { param.Position });
                            //    //object val1 = DeepClone(val);

                            //    //DebugLogger.Print(string.Format("val:{0}  val1:{1}", val, val1));
                            //    //current.SetValue(newClone, val1, new object[] { param.Position });
                            //}
                        }
                    }
                }
            }

            //while (tmpType != null)
            //{
            //    tmpType = tmpType.BaseType;
            //}
            return newClone;
        }

        static public bool IsArray(System.Type type)
        {
            if (!type.IsClass)
                return false;

            // 是否数组
            if (type.IsArray)
                return true;

            return false;
        }

        public static Array CloneArray(Array val)
        {
            Type elementType = val.GetType().GetElementType();
            Array val1 = Array.CreateInstance(elementType, val.Length);

            for (int i = 0; i < val.Length; i++)
            {
                val1.SetValue(DeepClone(val.GetValue(i)), i);

            }
            return val1;
        }

        static public bool IsList(System.Type type)
        {
            // 是否模板类型
            if (!type.IsGenericType)
                return false;

            if (typeof(IList).IsAssignableFrom(type))
                return true;

            return false;
        }

        public static IList CloneList(ICollection collection)
        {
            if (collection == null)
            {
                return null;
            }

            //var listType = typeof(List<>);
            //var constructedListType = listType.MakeGenericType(collection.GetType());
            //IList instance = Activator.CreateInstance(constructedListType) as IList;

            Type readOnlyCollectionContentsType = collection.GetType().GetGenericArguments()[0];
            IList instance = CreateGeneric(typeof(List<>), readOnlyCollectionContentsType) as IList;
            IEnumerator e = collection.GetEnumerator();
            while (e.MoveNext())
            {
                object val = DeepClone(e.Current);
                instance.Add(val);
            }

            //readOnlyCollectionContentsType.GetInterface("IList", true);
            //readOnlyCollectionContentsType.GetInterface("IDictionary", true);
            //readOnlyCollectionContentsType.GetInterface("ICollection", true);
            //readOnlyCollectionContentsType.GetInterface("IEnumerable", true);
            //readOnlyCollectionContentsType.GetInterface("ICloneable", true);
            //readOnlyCollectionContentsType.GetInterface("ISerializable", true);

            return instance;
        }

        static public bool IsDictionary(System.Type type)
        {
            if (!type.IsGenericType)
                return false;

            System.Type genericDefinition = type.GetGenericTypeDefinition();
            //DebugLogger.Print(genericDefinition.ToString ());

            // IsAssignableFrom 确定当前的 Type 的实例是否可以从指定 Type 的实例分配
            if (typeof(IDictionary).IsAssignableFrom(type))
                return true;

            Type genericInterfaceDefinition = typeof(IDictionary<,>);
            foreach (Type i in type.GetInterfaces())
            {
                Type interfaceDefinition = i.GetGenericTypeDefinition();
                if (genericInterfaceDefinition == interfaceDefinition)
                {
                    return true;
                }
            }

            return (genericDefinition == typeof(Dictionary<,>));
        }

        public static IDictionary CloneDictionary(IDictionary collection)
        {
            if (collection == null)
            {
                return null;
            }

            IDictionary newDict = NewDictionary(collection);
            IDictionaryEnumerator e = collection.GetEnumerator();
            while (e.MoveNext())
            {
                object key = DeepClone(e.Key);
                object val = DeepClone(e.Value);

                newDict.Add(key, val);
            }

            return newDict;
        }

        public static IDictionary NewDictionary(IDictionary collection)
        {
            Type collType = collection.GetType();
            Type genericCollectionDefinitionType = collection.GetType();

            Type keyType = genericCollectionDefinitionType.GetGenericArguments()[0];
            Type valueType = genericCollectionDefinitionType.GetGenericArguments()[1];

            Type dictGenericType = typeof(Dictionary<,>);
            IDictionary dict = CreateGeneric(dictGenericType, keyType, valueType) as IDictionary;
            //IDictionary dict = Newtonsoft.Json.Utilities.ReflectionUtils.CreateGeneric(dictGenericType, keyType, valueType) as IDictionary;
            return dict;
        }

        public static object CreateGeneric(Type genericTypeDefinition, Type innerType, params object[] args)
        {
            return CreateGeneric(genericTypeDefinition, new[] { innerType }, args);
        }

        public static object CreateGeneric(Type genericTypeDefinition, Type innerType, Type innerType2, params object[] args)
        {
            return CreateGeneric(genericTypeDefinition, new[] { innerType, innerType2 }, args);
        }

        public static object CreateGeneric(Type genericTypeDefinition, IList<Type> innerTypes, params object[] args)
        {
            //var constructedListType = genericTypeDefinition.MakeGenericType(innerTypes.ToArray ());
            //IList instance = Activator.CreateInstance(constructedListType) as IList;
            //return instance;

            Type specificType = MakeGenericType(genericTypeDefinition, innerTypes.ToArray());
            return Activator.CreateInstance(specificType, args);
        }

        public static Type MakeGenericType(Type genericTypeDefinition, params Type[] innerTypes)
        {
            return genericTypeDefinition.MakeGenericType(innerTypes);
        }
        #endregion Deep clone

        #region UnityUtils
        // 和Unity相关的几个DLL操作
        static Assembly u3dCSAsm = null;
        static Assembly u3dCSEditorAsm = null;
        static Assembly u3dFirstPassCSAsm = null;
        static Assembly mProtoCSAsm = null;
        public static System.Object CreateInstance(System.Type type, params object[] args)
        {
            System.Object retObj = System.Activator.CreateInstance(type, args);
            return retObj;
        }

        public static System.Object CreateInstance(string type, params object[] args)
        {
            Type siteAsmName = GetUnityAssembly().GetType(type);
            System.Object retObj = System.Activator.CreateInstance(siteAsmName, args);
            return retObj;
        }

        public static System.Reflection.Assembly GetUnityAssembly()
        {
            if (null != u3dCSAsm)
            {
                return u3dCSAsm;
            }

            Assembly[] domainAsms = System.AppDomain.CurrentDomain.GetAssemblies();
            foreach (Assembly asm in domainAsms)
            {
                string asmFullName = asm.FullName;
                string asmName = asm.GetName().Name;

                if (asmName.Equals("Assembly-CSharp"))
                {
                    u3dCSAsm = asm;
                }
                else if (asmName.Equals("Assembly-CSharp-Editor"))
                {
                    u3dCSEditorAsm = asm;
                }
                else if (asmName.Equals("Assembly-CSharp-firstpass"))
                {
                    u3dFirstPassCSAsm = asm;
                }
                else if (asmName.Equals("ProtoModel"))
                {
                    mProtoCSAsm = asm;
                }
            }

            return u3dCSAsm;
        }

        public static System.Reflection.Assembly GetEditorAsm()
        {
            if (null != u3dCSEditorAsm)
            {
                return u3dCSEditorAsm;
            }

            Assembly[] domainAsms = System.AppDomain.CurrentDomain.GetAssemblies();
            foreach (Assembly asm in domainAsms)
            {
                string asmFullName = asm.FullName;
                string asmName = asm.GetName().Name;

                if (asmName.Equals("Assembly-CSharp"))
                {
                    u3dCSAsm = asm;
                }
                else if (asmName.Equals("Assembly-CSharp-Editor"))
                {
                    u3dCSEditorAsm = asm;
                }
                else if (asmName.Equals(""))
                {
                    u3dFirstPassCSAsm = asm;
                }
                else if (asmName.Equals("ProtoModel"))
                {
                    mProtoCSAsm = asm;
                }
            }

            return u3dCSEditorAsm;
        }

        static public bool IsScriptableObject(System.Type type)
        {
            if (!type.IsClass)
                return false;

            bool result = type.IsSubclassOf(typeof(ScriptableObject));
            return result;
        }

        static public object CloneScriptableObject(object other)
        {
            System.Type thisType = other.GetType();
            object newClone = ScriptableObject.CreateInstance(thisType);

            System.Type tmpType = thisType;
            if (tmpType != null)
            {
                FieldInfo[] fieldInfos = tmpType.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
                foreach (FieldInfo field in fieldInfos)
                {
                    object val = field.GetValue(other);

                    // 如果一个类中声明了同样类型的变量，且不为空，直接赋值，防止死循环
                    if (null != val && val.GetType() == tmpType)
                    {
                        object val1 = val;
                        field.SetValue(newClone, val1);
                    }
                    else
                    {
                        object val1 = DeepClone(val);
                        field.SetValue(newClone, val1);
                    }
                }

                PropertyInfo[] propInfos = tmpType.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
                foreach (PropertyInfo current in propInfos)
                {
                    if (current.CanRead && current.CanWrite)
                    {
                        ParameterInfo[] paramInfos = current.GetIndexParameters();
                        if (paramInfos.Length == 0)
                        {
                            object val = current.GetValue(other, null);

                            // 如果一个类中声明了同样类型的变量，且不为空，直接赋值，防止死循环
                            if (null != val && val.GetType() == tmpType)
                            {
                                //DebugLogger.Print("val:", val.GetType());
                                object val1 = val;
                                current.SetValue(newClone, val1, null);
                            }
                            else
                            {
                                object val1 = DeepClone(val);
                                current.SetValue(newClone, val1, null);
                            }
                        }
                        else
                        {
                            // 此处逻辑不处理
                        }
                    }
                }
                //tmpType = tmpType.BaseType;
            }

            return newClone;
        }
        #endregion UnityUtils
    }
}

